/*
** ssl_client.c
** 使用OpenSSL_1.1.1版本
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

int main()
{
	int i = 0;
    int ret = -1;
    SSL_CTX *ctx = NULL;
    struct sockaddr_in dest = {};
	int sock;
	SSL *ssl;
	char buf[1024*8] = {0};

	//加载SSL算法和错误字符串
    OpenSSL_add_ssl_algorithms();
    SSL_load_error_strings();
	
    //创建上下文(SSLv23_client_method)
    ctx = SSL_CTX_new(SSLv23_client_method());
    
    if (!ctx) {
		perror("Unable to create SSL context");
		ERR_print_errors_fp(stderr);
		exit(EXIT_FAILURE);
    }

	////设置Key和证书
    if (SSL_CTX_use_certificate_file(ctx, "client.crt", SSL_FILETYPE_PEM) <= 0) {
		ERR_print_errors_fp(stderr);
		exit(EXIT_FAILURE);
    }
    //证书密码
    SSL_CTX_set_default_passwd_cb_userdata(ctx, (void*)"123456789");
    
    /* 载入用户私钥 */
    if (SSL_CTX_use_PrivateKey_file(ctx, "client.key", SSL_FILETYPE_PEM) <= 0) {
		ERR_print_errors_fp(stderr);
		exit(EXIT_FAILURE);
    }

    /* 检查用户私钥是否正确 */
    if (!SSL_CTX_check_private_key(ctx)) {
		ERR_print_errors_fp(stderr);
		exit(EXIT_FAILURE);
    }
    
    if(!SSL_CTX_load_verify_locations(ctx, "ca.crt", NULL)){ 
		ERR_print_errors_fp(stderr);
		exit(EXIT_FAILURE);
    }

    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
	
	//创建Socket
    if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        perror("socket create failed");
		exit(EXIT_FAILURE);
    }
    
    bzero(&dest, sizeof(struct sockaddr_in));
    
    dest.sin_family = AF_INET;
    dest.sin_port = htons(9999); 
	dest.sin_addr.s_addr = htonl(INADDR_ANY);
	
    if(connect(sock, (struct sockaddr*)&dest, sizeof(dest)) != 0){
		perror("socket connect failed");
		exit(EXIT_FAILURE);
    }
    
	//创建SSL
    ssl = SSL_new(ctx);
    //关联SSL和Socket
    if(SSL_set_fd(ssl, sock) != 1)
    {
        perror("SSL_set_fd failed");
		exit(EXIT_FAILURE);
    }
    
	//进行TSL/SSL握手
    if(SSL_connect(ssl) == -1){
        ERR_print_errors_fp(stderr);
		exit(EXIT_FAILURE);
    }
	//读取数据
	ret = SSL_read(ssl, buf, 4);
	printf("ret=%d\n", ret);
	for(i = 0; i < 4; i++)
		printf("buf[%d] = %d\n", i, buf[i]);
	
	SSL_free(ssl);
	SSL_CTX_free(ctx);
	close(sock);
}